Vue3深入组件-提供/注入

通常,当我们需要将数据从父组件传递到子组件时,我们使用 props。想象一下这样的结构:你有一些深嵌套的组件,而你只需要来自深嵌套子组件中父组件的某些内容。在这种情况下,你仍然需要将 prop 传递到整个组件链中,这可能会很烦人。

对于这种情况,我们可以使用 provideinject 对。父组件可以作为其所有子组件的依赖项提供程序,而不管组件层次结构有多深。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这个数据。

Provide/inject scheme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<template>
<div class="provide">
<ProvideChild></ProvideChild>
</div>
</template>

<script>
import ProvideChild from "./ProvideChild.vue";
export default {
data() {
return {
todos: ["Feed a cat", "Buy tickets"],
};
},
// provide: {
// user: "John Doe",
// todoLength: this.todos.length // 将会导致错误 'Cannot read property 'length' of undefined`
// },
provide() {
return {
user: "John Doe",
todoLength: this.todos.length,
};
},
components: {
ProvideChild,
},
};
</script>

// ProvideChild
<template>
<div class="provide-child"></div>
</template>
<script>
export default {
inject: ["user","todoLength"],
created() {
console.log(`Injected property: ${this.user}`); // > 注入 property: John Doe
console.log(`Injected property: ${this.todoLength}`); // 2
},
};
</script>

要访问组件实例 property,我们需要将 provide 转换为返回对象的函数

处理响应性

在上面的例子中,如果我们更改了 todos 的列表,这个更改将不会反映在注入的 todoLength property 中。这是因为默认情况下,provide/inject 绑定是被动绑定。我们可以通过将 ref property 或 reactive 对象传递给 provide 来更改此行为。在我们的例子中,如果我们想对祖先组件中的更改做出反应,我们需要为我们提供的 todoLength 分配一个组合式 API computed property:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<template>
<div class="provide">
{{ todos.length }}
<button @click="todos.push('111')"></button>
<ProvideChild></ProvideChild>
</div>
</template>

<script>
import { computed } from "vue";
import ProvideChild from "./ProvideChild.vue";

export default {
data() {
return {
todos: ["Feed a cat", "Buy tickets"],
};
},
provide() {
return {
user: "John Doe",
todoLength: computed(() => this.todos.length),
};
},
components: {
ProvideChild,
},
};
</script>

// ProvideChild
<template>
<div class="provide-child"></div>
</template>
<script>
export default {
inject: ["user", "todoLength"],
created() {
console.log(`Injected property: ${this.user}`); // > 注入 property: John Doe
console.log(`Injected property: ${this.todoLength.value}`); // 2
},
watch: {
"todoLength.value"(cur, old) {
console.log(cur, old);
},
},
};
</script>

在这种情况下,对 todos.length 将正确反映在组件中,其中“todoLength”被注入。在响应式计算和侦听组合式 API 部分中阅读关于 reactive provide/inject 的更多信息。